//============================================================================
// Name        : effects_manager.cpp
// Author      : dfoelber
//============================================================================

// INCLUDES //
#include "effects_manager.h"
#include "util.h"
#include <iostream>
#include <sstream>
//////////////

EffectsManager* EffectsManager::instance = NULL;

/**
* Private Constructor
**/
EffectsManager::EffectsManager()
{
}

/**
* Deconstructor
**/
EffectsManager::~EffectsManager()
{
	for( int i = 0; i < (int)effects.size(); i++ )
	{
		delete effects[i];
	}
}

/**
* Singleton accessor
* 
* @return Singleton instance
**/
EffectsManager* EffectsManager::GetManager()
{
	if( instance == NULL )
	{
		instance = new EffectsManager();
	}
	
	return instance;
}

/**
* Notifies of time passing.
* 
* @param The amount of time, in milliseconds, that passed since the last call
**/
void EffectsManager::TimePassed( int ms )
{
	for( int i = 0; i < (int)effects.size(); i++ )
	{
		if( effects[i]->state != EFFECT_STATE_LOADED )
		{
			effects[i]->lifetime += ms;
		}
	}
	
	//CheckPrereqs();
}

/**
* Adds a listener.
* 
* @param listener The listener
**/
void EffectsManager::AddListener(EffectsListener* listener)
{
	listeners.push_back(listener);
}

/**
* Processes the card and notifies listeners.
* 
* @param info The card info that is activated
* @param player If the source was the player
**/
void EffectsManager::CardActivated(CardInfo* info, bool player)
{
	vector<Effect*> copies;
	for( int i = 0; i < (int)info->GetEffects().size(); i++ )
	{
		int level = info->GetEffects()[i]->level;
		// If the effect passes the minimum level requirement
		vector<string> prereqs = Util::Split(info->GetEffects()[i]->GetPrereq(), '|');
		bool passed = true;
		for( int p = 0; p < (int)prereqs.size(); p++ )
		{
			string prereq = "";
			vector<string> tokens = Util::Split(prereqs[p], ',');
			if( tokens.size() > 1 && tokens[0] == EFFECT_PREREQ_MIN_LEVEL )
			{
				if( atoi(tokens[1].c_str()) > level )
				{
					passed = false;
				}
			}
		}
		
		if( passed )
		{
			// Cap the level to the actual level
			if( info->GetEffects()[i]->GetId() == EFFECT_DIRECT_DAMAGE && 
					(int)((DirectDamageEffect*)info->GetEffects()[i])->GetDamage().size() < level )
				level = ((DirectDamageEffect*)info->GetEffects()[i])->GetDamage().size();
			else if( info->GetEffects()[i]->GetId() == EFFECT_DAMAGE_OVER_TIME && 
					(int)((DoTEffect*)info->GetEffects()[i])->GetDamage().size() < level )
				level = ((DoTEffect*)info->GetEffects()[i])->GetDamage().size();
			else if( info->GetEffects()[i]->GetId() == EFFECT_ALIGNMENT_CHANGE && 
					(int)((AlignmentChangeEffect*)info->GetEffects()[i])->GetChange().size() < level )
				level = ((AlignmentChangeEffect*)info->GetEffects()[i])->GetChange().size();
			
			if( level <= 0 )
				continue;
			else info->GetEffects()[i]->level = level - 1;
		
			// Next, separate the effect into effects with a single target
			vector<string> targets = Util::Split(info->GetEffects()[i]->GetTarget(), '|');
			for( int t = 0; t < (int)targets.size(); t++ )
			{
				Effect* effect = info->GetEffects()[i]->Copy();
				
				// give the effect a uid.
				int random = 0;
				bool contains;
				do
				{
					contains = false;
					random = rand() % 10000;
					for( int e = 0; e < (int)copies.size(); e++ )
					{
						if( copies[e]->uid == random )
							contains = true;
					}
				}
				while( contains );
				
				effect->uid = random;
				effect->oldPos = i;
				effect->SetTarget(targets[t]);
				
				// set casting animation
				effect->useCastAnimation = false;
				if( effect->GetPrereq() == EFFECT_PREREQ_NONE )
				{
					effect->useCastAnimation = true;
					for( int e = 0; e < (int)copies.size(); e++ )
					{
						if( copies[e]->oldPos == effect->oldPos )
						{
							effect->useCastAnimation = false;
							ostringstream pos;
							pos << copies[e]->oldPos;
							ostringstream uid;
							uid << copies[e]->uid;
							effect->associations.insert(pair<string,string>(pos.str(),uid.str()));
							
							string prereq = EFFECT_PREREQ_LOCAL;
							prereq += ","; 
							prereq += pos.str();
							prereq += ",0,";
							prereq += EFFECT_STATE_ANIMATION_STARTED;
							effect->SetPrereq( prereq );
						}
					}
				}
				
				copies.push_back(effect);
			}
		}
	}
	
	for( int i = 0; i < (int)copies.size(); i++ )
	{
		vector<string> target = Util::Split(copies[i]->GetTarget(), ',');
		
		// if a local prereq, set to location of effect
		vector<string> prereqs = Util::Split(copies[i]->GetPrereq(), '|');
		string pres = "";
		for( int p = 0; p < (int)prereqs.size(); p++ )
		{
			string prereq = "";
			vector<string> tokens = Util::Split(prereqs[p], ',');
			if( tokens.size() > 1 && tokens[0] == EFFECT_PREREQ_LOCAL )
			{
				int index;
				stringstream ss(tokens[1]);
				ss >> index;
				for( int e = 0; e < (int)copies.size(); e++ )
				{
					// default all association on the first instance
					if( e != i && copies[e]->oldPos != copies[i]->oldPos && index == copies[e]->oldPos && 
							copies[i]->associations.size() == 0 )
					{
						ostringstream oss;
						oss << copies[index]->uid;
						copies[i]->associations.insert(pair<string,string>(tokens[1],oss.str()));
					}
					else if( e != i && copies[e]->oldPos != copies[i]->oldPos && index == copies[e]->oldPos && (int)target.size() > 1 &&
							(int)(Util::Split(copies[e]->GetTarget(), ',').size()) > 1 &&
							Util::Split(copies[e]->GetTarget(), ',')[1] == target[1] )
					{
						ostringstream oss;
						oss << copies[index]->uid;
						copies[i]->associations.insert(pair<string,string>(tokens[1],oss.str()));
					}	
				}
			}
			
			for( int t = 0; t < (int)tokens.size(); t++ )
			{
				prereq += tokens[t] + ",";
			}
			prereq.resize(prereq.size() - 1);
			pres += prereq + "|";
		}
		pres.resize(pres.size() - 1);
		copies[i]->SetPrereq(pres);
		
		if(player)
			copies[i]->origin = EFFECT_TARGET_SELF;
		else
			copies[i]->origin = EFFECT_TARGET_ENEMY;
		
		if(!player)
		{
			if( copies[i]->GetTarget().find(EFFECT_TARGET_ENEMY) != string::npos )
			{
				copies[i]->SetTarget(Util::Replace(copies[i]->GetTarget(), EFFECT_TARGET_ENEMY, EFFECT_TARGET_SELF));
			}
			else if( copies[i]->GetTarget().find(EFFECT_TARGET_SELF) != string::npos )
			{
				copies[i]->SetTarget(Util::Replace(copies[i]->GetTarget(), EFFECT_TARGET_SELF, EFFECT_TARGET_ENEMY));
			}
		}
		
		copies[i]->state = EFFECT_STATE_LOADED;
	}
	
	effects.insert( effects.end(), copies.begin(), copies.end() );
	
	CheckPrereqs();
}

/**
* Notifies the manager that the effect has changed state.
* 
* @param effect The effect
* @param state The new state
**/
void EffectsManager::ChangeEffectState(Effect* effect, string state)
{
	for( int i = 0; i < (int)effects.size(); i++ )
	{
		if( effects[i]->uid == effect->uid )
		{
			effects[i]->state = state;
			
			for( int l = 0; l < (int)listeners.size(); l++ )
			{
				listeners[l]->EffectStateChanged(effects[i]);
			}
			
			CheckPrereqs();
			return;
		}
	}
}

/**
* Reverses the target and origin of an effect (recursive to all dependent effects).
* 
* @param effect The effect to reverse
**/
void EffectsManager::ReverseTarget(Effect* effect)
{
	vector<string> target = Util::Split( effect->GetTarget(), ',' );
	if( (int)target.size() > 1 )
	{
		string newTarget = target[0];
		newTarget += ",";
		newTarget += effect->origin;
		effect->SetTarget(newTarget);
		effect->origin = target[1];
	}
	
	for( int i = 0; i < (int)effects.size(); i++ )
	{
		if( effects[i] != effect )
		{
			vector<string> prereqs = Util::Split(effects[i]->GetPrereq(), '|');
			for( int p = 0; p < (int)prereqs.size(); p++ )
			{
				vector<string> tokens = Util::Split(prereqs[p], ',');
				if( tokens.size() > 1 && tokens[0] == EFFECT_PREREQ_LOCAL )
				{
					string sid = effects[i]->associations[tokens[1]];
					if( sid != "" && effect->uid == atoi(sid.c_str()) )
					{
						ReverseTarget(effects[i]);
					}
				}
			}
		}
	}
}

/**
* Accesses the prerequisites for each effect and starts them if they are ready.
**/
void EffectsManager::CheckPrereqs()
{
	for( int i = 0; i < (int)effects.size(); i++ )
	{
		if( effects[i]->state == EFFECT_STATE_LOADED )
		{
			if( effects[i]->GetPrereq() == EFFECT_PREREQ_NONE )
			{
				effects[i]->state = EFFECT_STATE_READY;
				for( int l = 0; l < (int)listeners.size(); l++ )
				{
					listeners[l]->EffectStateChanged(effects[i]);
				}
			}
			else
			{
				vector<string> prereqs = Util::Split(effects[i]->GetPrereq(), '|');
				bool passed = true;
				for( int p = 0; p < (int)prereqs.size(); p++ )
				{
					vector<string> tokens = Util::Split(prereqs[p], ',');
					if( tokens.size() > 1 && tokens[0] == EFFECT_PREREQ_LOCAL )
					{
						for( int e = 0; e < (int)effects.size(); e++ )
						{
							string id = "-1";
							ostringstream oss;
							oss << effects[e]->uid;
							
							id = effects[i]->associations[tokens[1]];
							
							if( tokens.size() > 3 && oss.str() == id )
							{
								int lifetime;
								stringstream ss(tokens[2]);
								ss >> lifetime;
								if( effects[e]->lifetime < lifetime || 
									(tokens[3] == EFFECT_STATE_READY && effects[e]->state == EFFECT_STATE_LOADED) ||
									(tokens[3] == EFFECT_STATE_ANIMATION_STARTED && effects[e]->state == EFFECT_STATE_READY) ||	
									(tokens[3] == EFFECT_STATE_ANIMATION_STARTED && effects[e]->state == EFFECT_STATE_LOADED) ||
									(tokens[3] == EFFECT_STATE_ANIMATION_DONE && effects[e]->state == EFFECT_STATE_ANIMATION_STARTED) || 
									(tokens[3] == EFFECT_STATE_ANIMATION_DONE && effects[e]->state == EFFECT_STATE_LOADED) ||
									(tokens[3] == EFFECT_STATE_ANIMATION_DONE && effects[e]->state == EFFECT_STATE_READY) ||
									effects[e]->state == EFFECT_STATE_FINISHED )
								{
									passed = false;
									break;
								}
							}
							
							oss.clear();
						}
					}
				}
				
				if( passed )
				{
					effects[i]->state = EFFECT_STATE_READY;
					for( int l = 0; l < (int)listeners.size(); l++ )
					{
						listeners[l]->EffectStateChanged(effects[i]);
					}
				}
			}
		}
	}
}
